<?php
namespace Phad;
trait Submission {
/** The sql row id from the last INSERT executed through Submission */
public $lastInsertId;
/** a pattern such as `/blog/{slug}/{id}/` to redirect to when a form completes submission.
* Typically the target is loaded from the html node like `<form target="/blog/{slug}/{id}/>`
*/
public $target_url;
/**
* @issue(jan 31, 2022) this function uses `header()` & then `exit`s (unless you set `$phad->exit_on_redirect = false`) ... it works for now, but it might end up bein g a problem
*/
public function redirect($ItemInfo, array $ItemRow){
$target = $this->getSubmitTarget($ItemInfo, $ItemRow);
if ($target===null)return;
header("Location: {$target}", true);
if ($this->exit_on_redirect){
exit;
}
}
/**
*
* @override for submission-handling
* @return true/false for "allow submit"/"don't allow submit"
*/
public function onSubmit($ItemInfo, &$ItemRow){
return true;
}
/**
* @return true to proceed with deletion or false to stop
* @override for submission-handling
*/
public function onWillDelete($ItemInfo){
return true;
}
public function did_delete($ItemInfo){
$id = $ItemInfo->args['id'];
if (!isset($ItemInfo->diddelete)||is_bool($ItemInfo->diddelete)){
echo "\nSuccessfully deleted ".$ItemInfo->name." with id '$id'";
return;
}
$functions = $this->parse_functions($ItemInfo->diddelete);
foreach ($functions as $fn=> /* string */ $args){
if ($fn=='print'){
echo $args;
} else if ($fn=='goto'){
//@todo allow paramaterized goto, maybe???
header("Cache-Control: no-cache");
header("Location: ".$args);
if ($this->exit_on_redirect){
exit;
}
} else if ($fn=='call'){
$this->call($args, $ItemInfo);
}
}
}
public function delete($ItemInfo){
if (!$this->onWillDelete($ItemInfo)){
if (count($errors)==0){
$ItemInfo->submit_errors[] = ['msg'=>"Deletion failed. Reason unkown. 'onDelete' error."];
}
return false;
}
$errors = &$ItemInfo->submit_errors;
$errors = [];
$table = $ItemInfo->table ?? strtolower($ItemInfo->name);
$check_stmt = $this->pdo->prepare($sql="SELECT * from $table WHERE `id` = :id LIMIT 2");
$check_stmt->execute(['id'=>$ItemInfo->args['id']]);
$rows = $check_stmt->fetchAll(\PDO::FETCH_ASSOC);
if (count($rows)==0){
$msg = "Cannot delete '".$ItemInfo->name."'. None exist with id '".$ItemInfo->args['id']."'";
$errors[] = ['msg'=>$msg];
echo $msg;
return false;
} else if (count($rows)>1){
$msg = "Cannot delete '".$ItemInfo->name."'. Multiple exist with id '".$ItemInfo->args['id']."'. Database Error.";
$errors[] = ['msg'=>$msg];
echo $msg;
return false;
}
$submitter = new \Phad\PDOSubmitter($this->pdo);
try {
$didSubmit = $submitter->delete($table, $ItemInfo->args['id']);
} catch (\PDOException $pe){
$errors[] = ['msg'=>"Deletion was allowed, but there was an unknown technical error. Error code '".$this->pdo->errorCode()."'"];
echo "Deletion was allowed, but there was an unknown technical error. Error code '".$this->pdo->errorCode()."'";
return false;
}
if (!$didSubmit){
$query = new \Phad\Query();
$ii = (array)$ItemInfo;
unset($ii['args']);
var_dump($ii);
exit;
// $args = $ItemInfo->args;
// unset($args['phad']);
// var_dump($args);
// exit;
$items = $query->get($ItemInfo->name, ['type'=>'default'], $ItemInfo->args, $ItemInfo->type, null);
$item = $this->
// $query_info
$errors[] = ['msg'=>"Deletion was allowed, but there was an unknown technical error. Error code '".$this->pdo->errorCode()."'"];
echo "Deletion was allowed, but there was an unknown technical error. Error code '".$this->pdo->errorCode()."'";
return false;
}
return true;
}
/**
* Convert the array of failed columsn returned by Form Validator's validate() method into a nice array of [ [ 'msg'=>'erro rmessages'],['msg'=>'another error'] ]
*/
public function make_error_messages(array $errors): array{
$msgs = [];
foreach ($errors as $prop=>$e){
if ($e['failed_value']==''&&$e['required']){
$msgs[]['msg'] = "'$prop' is required";
}
//@TODO handle other error cases
}
return $msgs;
}
/**
* If the submit is an `INSERT`, then `$ItemRow` will have its `id` set
*
* @param &$ItemInfo the item data object
* @param &$ItemRow key=>value array.
*/
public function submit($ItemInfo, array &$ItemRow): bool {
if (!$this->onSubmit($ItemInfo, $ItemRow)){
if (count($errors)==0){
$ItemInfo->submit_errors[] = ['msg'=>"Validation failed. Reason unkown. 'onSubmit' error."];
}
return false;
}
$errors = &$ItemInfo->submit_errors;
$validator = new \Phad\FormValidator($ItemInfo->properties, $errors);
$validator->validators = &$this->validators;
$errors = [];
if ($validator->validate($ItemRow, $errors, $failed_columns)===false){
if (count($errors)==0){
$errors[] = ['msg'=>"Validation failed. Reason unkown. 'validation' error"];
}
return false;
}
$submitter = new \Phad\PDOSubmitter($this->pdo);
try {
// echo 'will try submit';
$didSubmit = $submitter->submit($ItemInfo->table ?? strtolower($ItemInfo->name), $ItemRow);
// print_r($ItemRow);
// exit;
} catch (\PDOException $pe){
if ($this->router->lia->debug){
$errors[] = ['msg'=>"Submission was valid, but there was an unknown technical error. Error '".$pe->getMessage()."'"];
} else {
$errors[] = ['msg'=>"Submission was valid, but there was an unknown technical error. Error code '".$this->pdo->errorCode()."'"];
}
error_log($pe.'');
return false;
}
if (!$didSubmit){
if ($this->router->lia->debug){
$errors[] = ['msg'=>"Submission was valid, but there was an unknown technical error. Error '".$pe->getMessage()."'"];
} else {
$errors[] = ['msg'=>"Submission was valid, but there was an unknown technical error. Error code '".$this->pdo->errorCode()."'"];
}
return false;
}
$this->lastInsertId = $submitter->lastInsertId();
if (is_numeric($this->lastInsertId)){
$ItemRow['id'] = $ItemRow['id'] ?? $this->lastInsertId;
}
return true;
}
/**
*
* @issue(jan 31, 2022) this method is stupid & should only take in what it needs at minimum (target + args, probably)
*/
protected function getSubmitTarget($ItemInfo, $ItemRow){
$target = $ItemInfo->target
?? $this->target_url
// ?? '/';
?? null;
if ($target==null)return null;
$target = $this->fillDynamicTarget($target, $ItemInfo, $ItemRow);
$target = $this->normalizeTarget($target);
return $target;
}
/**
*
* @issue(jan 31, 2022) this method is stupid & should only take in what it needs at minimum (target + args, probably) ... and WHY am i getting object from row? instead of just using the item row ... ohhhh ... say, the object has a magical `slug` property, but the ROW does not store a slug .... that makes objectFromRow necessary ... which makes this all a bit less stupid.
* @issue(jan 31, 2022) this depends upon liaison's router & i don't like that ... i'd rather use handlers & then temporarily use liaison's router that way ... but idk ... it's okay for now
*/
protected function fillDynamicTarget($targetUrl, $ItemInfo, $ItemRow){
$url = parse_url($targetUrl);
$router = $this->router;
$parsed = $router->decode_pattern($url['path']);
if (count($parsed['params'])==0)return $targetUrl;
$ItemObj = $this->object_from_row($ItemRow, $ItemInfo);
$fillWith = [];
foreach ($parsed['params'] as $i=>$prop){
$fillWith[$prop] = $ItemObj->$prop;
}
$newPath = $router->decoded_pattern_to_url($parsed, $fillWith);
return $newPath;
}
/**
* For urls containing protocol://domain.tld, just return the target
* else add the current protocol://domain.tld
*/
protected function normalizeTarget($targetUrl){
$url = parse_url($targetUrl);
if (isset($url['host'])&&isset($url['scheme'])){
return $targetUrl;
}
if (substr($targetUrl,0,1)!='/')$targetUrl = '/'.$targetUrl;
$host = $_SERVER['HTTP_HOST'];
$protocol = empty($_SERVER['HTTPS']) ? 'http://' : 'https://';
$targetUrl = str_replace('//','/', $targetUrl);
return $protocol.$host.$targetUrl;
}
/**
* @example `foreach ($phad->getHeaders() as $header){header(...$header);}`
* @return array of headers.
*/
// public function getHeaders(): array{
// return $this->headers;
// }
}